/**
 * Copyright(c) by JNet Smartware Group.
 * All rights reserved. Not distribution or redistribution without
 * any permission of JNet Smartware Group.
 * 
 * @author thuc
 * 2010/03/15
 */
package vn.smartware.co.java.net.commons;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * FileUtil
 * @author thuctt
 */
public final class FileUtil {

	private static Log log = LogFactory.getLog(FileUtil.class);
	
	/**
	 * get file list
	 * @param strDir
	 * @param ext
	 * @return ArrayList
	 */
	public static ArrayList<String> getFiles(String strDir, final String ext) {

		return getFiles(strDir, ext, null);
	}

	/**
	 * get file list
	 * @param strDir
	 * @param ext
	 * @param pattern 
	 * @return ArrayList
	 */
	public static ArrayList<String> getFiles(String strDir, final String ext,
			String pattern) {

		ArrayList<String> list1 = new ArrayList<String>();
		ArrayList<String> list2 = new ArrayList<String>();
		
		String dirPath = formatPath(strDir);
		
		File dir = new File(dirPath);

		if (!dir.isDirectory()) {
			System.out.println("[ERROR]{FileUtil-getFiles} Folder: " + dirPath
					+ "} not exist.");
			return new ArrayList<String>();
		}

		File[] files = null;
		files = dir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				if (!Utils.isNullEmpty(ext)) {
					return name.toUpperCase().endsWith(ext.toUpperCase());
				}
				return true;
			}
		});

		//
		if (files == null || files.length == 0) {
			return new ArrayList<String>();
		}
		log.info("Folder: " + dirPath);
		for (int i = 0; i < files.length; i++) {
			if (!Utils.isNullEmpty(pattern)
					&& files[i].getName().indexOf(pattern) != -1) {
				list1.add(dirPath + files[i].getName());
				log.info(files[i].getName());
			} else {
				list2.add(dirPath + files[i].getName());
				log.info(files[i].getName());
			}
		}
		if (!Utils.isNullEmpty(pattern)) {
			return list1;
		}
		return list2;
	}
	
	/**
	 * format path of file
	 * @param path
	 * @return String
	 */
	public static String formatPath(String path) {

		String ret = path.replace('\\', '/');
		if (ret.lastIndexOf("/") != ret.length() - 1) {
			ret += "/";
		}
		return ret;
	}
	
	/**
	 * get file list start with a specified prefix
	 * @param strDir
	 * @param ext
	 * @param prefix 
	 * @return ArrayList
	 */
	public static ArrayList<String> getFilesStartWith(String strDir, final String ext,
			String prefix) {

		if (Utils.isNullEmpty(prefix)){
			System.out.println("[ERROR] {FileUtil-getFilesStartWith} prefix is null.");
			return new ArrayList<String>();
		}
		
		String dirPath = formatPath(strDir);
		
		File dir = new File(dirPath);
		if (!dir.isDirectory()) {
			System.out.println("[ERROR]{FileUtil-getFilesStartWith} Folder: " + dirPath
					+ "} not exist.");
			return new ArrayList<String>();
		}
		
		log.info("Folder: " + dirPath);
		
		ArrayList<String> list = new ArrayList<String>();

		File[] files = dir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return name.toUpperCase().endsWith(ext.toUpperCase());
			}
		});

		//
		if (files == null || files.length == 0) {
			return new ArrayList<String>();
		}

		for (int i = 0; i < files.length; i++) {
			if (files[i].getName().startsWith(prefix)) {
				list.add(files[i].getName());
				log.info(files[i].getName());
			}
		}
		return list;
	}

	/**
	 * get file list
	 * @param strDir
	 * @param ext
	 * @return ArrayList
	 */
	public static ArrayList<String> getFilesExclude(String strDir, final String ext) {

		String dirPath = formatPath(strDir);
		
		File dir = new File(dirPath);

		if (!dir.isDirectory()) {
			log.info("[ERROR] {FileUtil-getFilesExclude} Folder: " + dirPath + " not exist.");
			return null;
		}

		File[] files = dir.listFiles(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				return !name.toUpperCase().endsWith(ext.toUpperCase());
			}
		});

		ArrayList<String> list = new ArrayList<String>();
		if (null != files && files.length > 0) {
			for (int i = 0; i < files.length; i++) {
				list.add(dirPath + files[i].getName());
			}
		}

		return list;
	}

	/**
	 * make new file
	 * @param fileName
	 * @return boolean
	 */
	public static boolean makeNewFile(String fileName) {

		File file = new File(fileName);
		boolean chk = false;

		try {
			chk = file.createNewFile();
		} catch (IOException e) {
			e.printStackTrace();
		}

		if (chk) {
			System.out.println("[DEBUG]{FileUtil-makeNewFile}" + fileName + " created.");
		}else{
			System.out.println("[DEBUG]{FileUtil-makeNewFile}" + fileName + "create failed.");
		}
		return chk;
	}

	/**
	 * delete file
	 * @param fileName 
	 * @return boolean
	 */
	public static boolean deleteFile(String fileName) {

		log.info("[INFO] {FileUtil-deleteFile} Deleting file: " + fileName);
		File file = new File(fileName);
		boolean flg = file.delete();
		if (flg){
			log.info("[INFO] {FileUtil-deleteFile} " + fileName + " deleted successfully.");
		}else{
			log.info("[INFO] {FileUtil-deleteFile} Delete " + fileName + " failed.");
		}
		return flg;
	}

	/**
	 * write an array list into a file
	 * @param fileName 
	 * @param content 
	 * @throws Exception 
	 */
	public static void write(String fileName,
			ArrayList<String> content) throws Exception {

		FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        BufferedWriter bw = null;
        
		try {
			fos = new FileOutputStream(fileName);
			osw = new OutputStreamWriter(fos, Const.CHAR_CODE_MS932);
			bw = new BufferedWriter(osw);
	
			for (int i = 0; i < content.size(); i++) {
				String line = (String) content.get(i);
				if (i != content.size() - 1) {
					line += Const.LINE_FEED_CRLF;
				}
				bw.write(line);
			}
			
		} catch (IOException e) {
			throw e;
			
		} finally {
			try{
				bw.close();
		        bw = null;
		        osw.close();
		        osw = null;
		        fos.close();
		        fos = null;
			} catch (IOException e) {
				throw e;
			} 
		}
	}
	
	/**
	 * write a line into a file. If file not exist, create a new file.
	 * Otherwise, append new line to existing content of file.
	 * <br>Character code: SHIFT-JIS
	 * <br>Line feed: CRLF
	 * 
	 * @param filename 
	 * @param msg 
	 * @throws Exception 
	 */
	public static void write(String filename, String msg) throws Exception {
		
		File file = new File(filename);
		if (!file.exists() || !file.isFile()){
			file.createNewFile();
		}
		
		FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        BufferedWriter bw = null;
		try {
	    	fos = new FileOutputStream(filename, true);
	        osw = new OutputStreamWriter(fos , Const.CHAR_CODE_SJIS);
	        bw = new BufferedWriter(osw);
	        
	        msg += Const.LINE_FEED_CRLF;
			bw.write(msg);
	        
		} catch (IOException e) {
			throw e;
			
		} finally {
			try{
				bw.close();
		        bw = null;
		        osw.close();
		        osw = null;
		        fos.close();
		        fos = null;
			} catch (IOException e) {
				throw e;
			} 
		}
	}

	/**
	 * write an array list into a file
	 * @param fileName 
	 * @param content 
	 * @param encoding 
	 * @param lineFeed 
	 * @throws IOException 
	 */
	public static void write(String fileName, ArrayList<String> content,
			String encoding, String lineFeed) throws IOException {

		File file = new File(fileName);
		if (file.isDirectory()) {
			System.out.println("[DEBUG]{FileUtil-write}" + fileName
					+ "not a file.");
			return;
		}
		FileOutputStream fos = new FileOutputStream(fileName);
		OutputStreamWriter osw = new OutputStreamWriter(fos, encoding);
		BufferedWriter bw = new BufferedWriter(osw);

		if (null != content) {
			for (int i = 0; i < content.size(); i++) {
				Object rec = content.get(i);
				String line = "";
				if (rec instanceof String[]) {
					String[] arr = (String[]) rec;
					line = Utils.toEmpty(arr[0]);
					for (int j = 1; j < arr.length; j++) {
						line += "," + Utils.toEmpty(arr[j]);
					}
				} else if (rec instanceof ArrayList<?>) {
					ArrayList<?> list = (ArrayList<?>) rec;
					line = (String) list.get(0);
					for (int j = 1; j < list.size(); j++) {
						line += "," + (String) list.get(j);
					}
				}

				if (i != content.size() - 1) {
					line += lineFeed;
				}
				bw.write(line);
			}
		}
		bw.close();
		bw = null;
		osw.close();
		osw = null;
		fos.close();
		fos = null;
	}

	/**
	 * @param srcPath
	 * @param destPath
	 * @return boolean
	 */
	public boolean copyFile(String srcPath, String destPath) {

		FileChannel srcChannel = null;
		FileChannel destChannel = null;
		try {
			srcChannel = new FileInputStream(srcPath).getChannel();
			destChannel = new FileOutputStream(destPath).getChannel();

			srcChannel.transferTo(0, srcChannel.size(), destChannel);

		} catch (Exception e) {
			System.out.println("[ERROR]{FileUtil-copyFile}"
					+ e.getMessage());
			e.printStackTrace();
			return false;

		} finally {
			try {
				srcChannel.close();
				destChannel.close();

			} catch (Exception e) {
				System.out.println("[ERROR]{FileUtil-copyFile}"
						+ e.getMessage());
				e.printStackTrace();
				return false;
			}
		}

		return true;
	}

	/**
	 * rename a file
	 * @param from
	 * @param to
	 * @return boolean
	 * @throws Exception
	 */
	public static boolean renameFile(String from, String to) throws Exception {
		File file = new File(from);
		if (!file.exists() || !file.isFile()) {
			return false;
		}
		return file.renameTo(new File(to));
	}

	/**
	 * convert file type
	 * @param fileList
	 * @param extFrom
	 * @param extTo
	 * @return boolean
	 * @throws Exception 
	 */
	public static boolean convertFiles(ArrayList<String> fileList, String extFrom,
			String extTo) throws Exception {

		boolean flg = true;
		try {
			for (int i = 0; i < fileList.size(); i++) {
				File file = new File((String) fileList.get(i));
				if (!file.exists() || !file.isFile()) {
					return false;
				}
				String nameFrom = file.getName();
				String nameTo = nameFrom.substring(0, nameFrom
						.lastIndexOf(extFrom))
						+ extTo;
				File newF = new File(file.getParentFile(), nameTo);
				if (!file.renameTo(newF)) {
					System.out.println("[ERROR]{FileUtil-convertFiles} Failed.");
					flg = false;
				} else {
					System.out.println("Renamed file from: " + nameFrom
							+ " To: " + nameTo);
				}
			}
		} catch (Exception e) {
			System.out.println("[ERROR]{FileUtil-convertFiles} Exception. " + e);
			e.printStackTrace();
		}
		return flg;
	}

	/**
	 * read content of file
	 * @param fileName
	 * @return ArrayList
	 */
	public static ArrayList<String[]> loadFile(String fileName) {

		if ((fileName == null) || (fileName == "")) {
			return new ArrayList<String[]>();
		}

		String line;
		ArrayList<String[]> list = new ArrayList<String[]>();

		try {
			BufferedReader in = new BufferedReader(new FileReader(fileName));
			while ((line = in.readLine()) != null) {
				String[] rec = line.split(",", -1);
				list.add(rec);
			}
			in.close();
		} catch (IOException e) {
			System.out.println(e.getMessage());
			return new ArrayList<String[]>();
		}
		return list;
	}

	/**
	 * get last modified date of a file
	 * @param filePath
	 * @return Date
	 * @throws Exception 
	 */
	public static Date getLastModified(String filePath) throws Exception {

		try {
			File file = new File(filePath);
			if (!file.exists() || !file.isFile()) {
				System.out.println("Input file not exist.");
				return null;
			}
			return new Date(file.lastModified());
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * check file exist
	 * @param fileName
	 * @return boolean
	 */
	public static boolean isFileExist(String fileName) {

		File file = new File(fileName);
		if (!file.exists() || !file.isFile()) {
			return false;
		}
		return true;
	}
	
	/**
	 * move file
	 * @param srcDir
	 * @param filename
	 * @param destDir
	 * @return boolean
	 */
	public static boolean moveFile(String srcDir, String filename, String destDir){
	
		srcDir = formatPath(srcDir);
		destDir = formatPath(destDir);
		
		File file = new File(srcDir + filename);
		if (!file.exists() || !file.isFile()) {
			return false;
		}
		log.info("[INFO]{FileUtil-moveFile} Moving file " + filename + " from " + srcDir + " to " + destDir);
		boolean flg = file.renameTo(new File(destDir + filename));
		if (flg){
			log.info("Successful.");
		}else{
			log.info("Failed.");
		}
		return flg;
	}
	
	/**
	 * zip a source file to a .ZIP file
	 * @param srcFilePath
	 * @param zipFilePath
	 * @return boolean
	 */
	public static boolean zipFile(String srcFilePath, String zipFilePath){
		
		File file = new File(srcFilePath);
		if (!file.exists() || !file.isFile()) {
			System.out.println("[ERROR]{FileUtil-zipFile} Source file not exist.");
			return false;
		}
		
		System.out.println("[INFO]{FileUtil-zipFile} Zipping file: " + zipFilePath);
		FileInputStream fis = null;
		ZipOutputStream out = null;
		try{
			fis = new FileInputStream(srcFilePath);
			out = new ZipOutputStream(new FileOutputStream(zipFilePath));
			out.setLevel(Deflater.DEFAULT_COMPRESSION);
			out.putNextEntry(new ZipEntry(srcFilePath.substring(srcFilePath.lastIndexOf("/")+1)));
			
			byte[] buffer = new byte[18024];
			int len;
			while ((len = fis.read(buffer)) > 0) {
				out.write(buffer, 0, len);
			}
			
		}catch (IllegalArgumentException  e) {
			System.out.println("[ERROR]{FileUtil-zipFile} IllegalArgumentException.");
			return false;
		}catch (IOException e) {
			System.out.println("[ERROR]{FileUtil-zipFile} IOException.");
			return false;
		}finally{
			try{
				fis.close();
				fis = null;
				out.closeEntry();
				out.finish();
				out.close();
				out = null;
				log.info("Successful");
			}catch (IOException e) {
				System.out.println("[ERROR]{FileUtil-zipFile} IOException.");
				return false;
			}
		}
		return true;
	}

	/**
	 * load file
	 * @param filename
	 * @return String
	 * @throws IOException 
	 */
	public static String load(String filename) throws IOException {
	
		File file = new File(filename);
		if (!file.exists() || !file.isFile()){
			return "";
		}
		
		String ret = "";
		FileInputStream fis = null;
        InputStreamReader reader = null;
        StringBuffer buffer = null;
		try {
			buffer = new StringBuffer();
			fis = new FileInputStream(filename);
			reader = new InputStreamReader(fis, Const.CHAR_CODE_SJIS);
			int c;
			while (true) {
				c = reader.read();
				if (c == -1){
					break;
				}
	            buffer.append((char) c);
	        }
			ret = buffer.toString();
	        
		} catch (IOException e) {
			throw e;
			
		} finally {
			try{
				reader.close();
				reader = null;
		        fis.close();
		        fis = null;
		        
			} catch (IOException e) {
				throw e;
			} 
		}
		return ret;
	}
}
